package com.young.fs.service.impl;

import com.young.common.config.SpringContextHolder;
import com.young.common.exception.BaseException;
import com.young.fs.exception.FsException;
import com.young.common.util.CommonUtil;
import com.young.common.util.StringUtils;
import com.young.fs.model.EFileType;
import com.young.fs.model.FileInfo;
import com.young.fs.service.IFileInfoService;
import com.young.fs.service.IFileService;
import com.young.fs.util.FsConfig;
import com.young.fs.util.FsConstants;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * 文件服务
 */
@Service("fsFileService")
//@SpringBootConfiguration
@Transactional
public class FileServiceImpl implements IFileService {

    private static final Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);

    public static String ROOT_PATH;//存储根路径

    public static String STORAGE_MODEL;//文件存储方式

    @Resource(name="fsFileInfoService")
    IFileInfoService fileInfoService;

    @Resource
    FsConfig fsConfig;
    /**
     * bean创建后执行此方法进行初始化
     */
    @PostConstruct
    public void init() {
        logger.info("[文件服务初始化] 初始化开始...");
        if (fsConfig == null){
            logger.warn("[文件服务初始化] 初始化异常:文件系统配置类FsConfig为null!");
            fsConfig = SpringContextHolder.getBean("fsConfig");
            if (fsConfig == null){
                throw new FsException("文件服务初始化失败:文件系统配置类FsConfig为null!");
            }
        }
        if ("/".equals(File.separator)){//linux
            ROOT_PATH = fsConfig.getRootPathAtLinux();//FsConfig.ROOT_PATH_LINUX;
            logger.info("[文件服务初始化] 当前操作系统为linux,初始化根目录为{}", ROOT_PATH);
        }else{//windows
            ROOT_PATH = fsConfig.getRootPathAtWindows();//FsConfig.ROOT_PATH_WINDOWS;
            logger.info("[文件服务初始化] 当前操作系统为windows,初始化根目录为{}", ROOT_PATH);
        }
        //判断根目录是否有效,无效则创建对应目录
        File file = new File(ROOT_PATH);
        if (file.exists()){//存在此文件/文件夹
            if (file.isDirectory()){//判断是否为文件夹
                logger.info("[文件服务初始化] 存在根目录!");
            }else{//名字为ROOT_PATH的文件,不是文件夹,则报错
                throw new FsException("文件服务初始化失败:配置的文件存储根目录\""+ROOT_PATH+"\"为文件类型,无法初始化文件夹!");
            }
        }else{//不存在,则创建
            file.mkdirs();
            logger.info("[文件服务初始化] 不存在此目录,成功执行目录创建!");
        }
        //文件存储方式
        STORAGE_MODEL = fsConfig.getFileStorageMode();//FsConfig.FILE_STORAGE_MODE;
        logger.info("[文件服务初始化] 文件存储方式为{}", fsConfig.getFileStorageMode());//FsConfig.FILE_STORAGE_MODE
        logger.info("[文件服务初始化] 初始化完成!");
    }

    /**
     * 保存单文件到默认目录，默认目录为根目录
     * @param file 文件信息
     * @return
     * @throws Exception
     */
    @Override
    public FileInfo saveFile(MultipartFile file, String businessCode) {
        if(StringUtils.isBlank(STORAGE_MODEL)){//判断文件存储方式是否为空
            throw new FsException("[保存文件失败] 文件存储方式为空!");
        }
        FileInfo folder = new FileInfo();
        folder.setId(FsConstants.ROOT_FOLDER_ID);//默认为根目录
        folder.setAddr(ROOT_PATH);//对应的根目录
        folder.setPath(FsConstants.ROOT_FOLDER_URL);
        return this.saveFile(file, folder, businessCode);
    }

    /**
     * 保存文件到指定文件夹下
     * @param file 文件信息
     * @param folderId 文件夹id
     * @return
     */
    @Override
    public FileInfo saveFile(MultipartFile file, String folderId, String businessCode) {
        //查询文件夹信息
        FileInfo folder = fileInfoService.getFolderInfo(folderId);
        if(folder == null){
            throw new FsException("文件夹["+folderId+"]找不到!");
        }
        if (StringUtils.isBlank(folder.getAddr())){
            throw new FsException("文件夹["+folderId+"]存储路径无效:"+folder.getAddr()+"!");
        }
        return this.saveFile(file, folder, businessCode);
    }


    /**
     * 存储文件,用于内部使用,尤其在批量处理,减少不必要的文件夹查询
     * 真正实现文件保存的方法,供其他方法调用
     * @param file 文件
     * @param folder 文件夹信息
     * @return
     */
    private FileInfo saveFile(MultipartFile file, FileInfo folder, String businessCode) {
        if (folder != null){
            if (StringUtils.isBlank(folder.getAddr())){
                throw new FsException("文件夹["+folder.getId()+"]存储路径无效:"+folder.getAddr()+"!");
            }
            if (file == null || file.isEmpty()){
                throw new FsException("待上传文件无效!");
            }
            String fileName = file.getOriginalFilename();//文件名
            String suffix = fileName.substring(fileName.lastIndexOf("."));//后缀名
            String realFileName = CommonUtil.getUUID() + suffix;//实际存储的文件名称
            String addr = folder.getAddr();//文件存储路径
            String path = folder.getPath();//url

            if(FsConfig.ACTUAL_PATH.equals(STORAGE_MODEL)) {//文件存储方式为实际存储
                //此方法没有指定文件夹目录，所以将保存到默认的根路径下
            }
            if(FsConfig.DATE_PATH.equals(STORAGE_MODEL)) {//文件存储方式日期存储
                //在该目录下创建年/月/日文件夹,防止单文件下的文件数过多
                Calendar c = Calendar.getInstance();
                addr += File.separator + c.get(Calendar.YEAR) + File.separator + (c.get(Calendar.MONTH)+1) + File.separator + c.get(Calendar.DAY_OF_MONTH);
                path += "/" + c.get(Calendar.YEAR) + "/" + (c.get(Calendar.MONTH)+1) + "/" + c.get(Calendar.DAY_OF_MONTH) + "/" + realFileName;
            }

            File dir = new File(addr);
            if (!dir.exists()){//若文件夹不存在,则创建
                dir.mkdirs();
            }
            File saveFile = new File(dir, realFileName);
            try {
                file.transferTo(saveFile);//写文件到对应路径
            } catch (IOException e) {
                logger.error("[保存文件] 保存文件到指定地址失败,地址={}", dir, e);
                throw new BaseException("保存文件失败");
            }
            //编辑文件信息写到数据库
            FileInfo fileInfo = new FileInfo();
            fileInfo.setId(CommonUtil.getUUID());//文件编号
            fileInfo.setFolderId(folder.getId());//所属文件夹编号
            fileInfo.setName(fileName);//文件名称
            fileInfo.setIsFile("1");//文件标识
            fileInfo.setStatus("1");//状态
            fileInfo.setType(EFileType.getTypeBySuffix(suffix));//文件类型
            fileInfo.setSize((int)(file.getSize()/1024));//文件大小,单位为kb
            fileInfo.setAddr(saveFile.getAbsolutePath());//实际存储地址
            fileInfo.setPath(path);
            fileInfo.setNum(10);//排序
            fileInfo.setBusinessCode(StringUtils.isBlank(businessCode) ? null : businessCode);
            fileInfoService.insertFileInfo(fileInfo);//插入文件记录
            logger.info("[文件信息保存成功] {}", fileInfo);
            return fileInfo;
        }else{
            throw new FsException("文件夹["+folder.getId()+"]找不到!");
        }
    }


    /**
     * 保存多文件到指定文件夹下
     * @param files 文件集合
     * @param folderId 文件夹编号
     * @return
     */
    @Override
    public List<FileInfo> saveFiles(List<MultipartFile> files, String folderId, String businessCode) {
        //查询文件夹信息,只查询一次
        FileInfo folder = fileInfoService.getFolderInfo(folderId);
        if (folder != null){
            List<FileInfo> list = new ArrayList<FileInfo>();
            for (MultipartFile file : files){
                list.add(this.saveFile(file, folder, businessCode));
            }
            return list;
        }else{
            throw new FsException("文件夹["+folder.getId()+"]找不到!");
        }
    }

    /**
     * 删除文件
     * @param fileId 文件编号
     * @return
     */
    @Override
    public boolean dropFile(String fileId) {
        FileInfo fileInfo = fileInfoService.getFileInfo(fileId);
        if (fileInfo != null ){
            if (fileInfo.getAddr() != null){
                File file = new File(fileInfo.getAddr());
                if (file.exists()){
                    if (file.isDirectory()){
                        logger.info("[文件删除] 删除失败:暂不支持文件夹删除操作!");
                        return false;
                    }else{
                        file.delete();//删除文件
                        logger.info("[文件删除] 成功删除文件{}!", fileInfo.getAddr());
                    }
                }else{
                    logger.info("[文件删除] 文件实体不存在!");
                }
                fileInfoService.deleteFileInfo(new String[]{fileId});//删除文件信息
            }else{
                logger.info("[文件删除] 删除失败:文件地址信息无效:{}", fileInfo.getAddr());
                return false;
            }
        }
        return true;
    }

    /**
     * 删除文件夹
     * @param folderId 文件夹编号
     * @return
     */
    @Override
    public boolean dropFolder(String folderId) {
        //查询文件夹信息
        FileInfo folder = fileInfoService.getFileInfo(folderId);
        if (folder != null){
            if (folder.getAddr() != null){
                //删除整个文件夹
                File file = new File(folder.getAddr());
                try {
                    FileUtils.deleteDirectory(file);
                } catch (IOException e) {
                    logger.error("[删除文件夹] 删除文件夹出错,文件夹={}", folder.getAddr(), e);
                    throw new BaseException("删除文件夹出错");
                }
                logger.info("[文件夹删除] 删除的文件夹路径为:{}", folder.getAddr());
                //删除文件夹下的文件信息
                fileInfoService.deleteFileInfoByFolder(folderId);
                //删除文件夹信息
                fileInfoService.deleteFileInfo(new String[]{folderId});
            }else{
                logger.info("[文件夹删除] 删除失败:文件夹地址信息无效:{}", folder.getAddr());
                return false;
            }
        }
        return true;
    }

    /**
     * 创建文件夹
     * @param folder 文件夹信息
     * @return
     */
    @Override
    public FileInfo createFolder(FileInfo folder) {
        folder.setId(CommonUtil.getUUID());
        //查询上级文件夹信息
        FileInfo parent = fileInfoService.getFileInfo(folder.getFolderId());
        if(parent != null){
            //新建文件夹
            File file = new File(parent.getAddr(), folder.getName());
            if (file.exists()){//如果存在该名称
                throw new FsException("当前目录下已存在名为["+folder.getName()+"]的文件夹!");
            }
            file.mkdirs();
            logger.info("[新建文件夹] 文件夹路径为:{}", file.getPath());

            //保存文件夹信息
            folder.setIsFile("0");//0表示文件夹
            folder.setStatus("1");//正常状态
            folder.setAddr(file.getPath());//路径
            folder.setPath(parent.getPath() + "/" + folder.getName());
            folder.setType(EFileType.FOLDER);//文件夹类型
            folder.setNum(parent.getNum()+10);
            fileInfoService.insertFileInfo(folder);
        }else{
            throw new FsException("父文件夹["+folder.getFolderId()+"]不存在!");
        }
        return folder;
    }

}
